#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <pcap.h>

FILE* file;
struct ether_header
{
	/* 目的以太网地址 */
	u_int8_t ether_dhost[6];
	/* 源以太网地址 */
	u_int8_t ether_shost[6];
	/* 以太网类型 */
	u_int16_t ether_type;
};
/* 下面是IP地址格式 */
typedef u_int32_t in_addr_t;
struct in_addr
{
	in_addr_t s_addr;
};

struct ip_header
{
#ifdef WORDS_BIGENDIAN
	u_int8_t ip_version: 4,  /* IP协议版本 */
		 ip_header_length: 4; /* IP协议首部长度 */
#else
	u_int8_t ip_header_length: 4, ip_version: 4;
#endif
	/* TOS服务质量 */
	u_int8_t ip_tos;
	/* 总长度 */
	u_int16_t ip_length;
	/* 标识 */
	u_int16_t ip_id;
	/* 偏移 */
	u_int16_t ip_off;
	/* 生存时间 */
	u_int8_t ip_ttl;
	/* 协议类型 */
	u_int8_t ip_protocol;
	/* 校验和 */
	u_int16_t ip_checksum;
	/* 源IP地址 */
	struct in_addr ip_souce_address;
	/* 目的IP地址 */
	struct in_addr ip_destination_address;
};


//tcp
typedef struct _tcp_hdr  
{  
	unsigned short src_port;    //源端口号  
	unsigned short dst_port;    //目的端口号  
	unsigned int seq_no;        //序列号  
	unsigned int ack_no;        //确认号  
#if LITTLE_ENDIAN  
	unsigned char reserved_1:4; //保留6位中的4位首部长度  
	unsigned char thl:4;        //tcp头部长度  
	unsigned char flag:6;       //6位标志  
	unsigned char reseverd_2:2; //保留6位中的2位  
#else  
	unsigned char thl:4;        //tcp头部长度  
	unsigned char reserved_1:4; //保留6位中的4位首部长度  
	unsigned char reseverd_2:2; //保留6位中的2位  
	unsigned char flag:6;       //6位标志   
#endif  
	unsigned short wnd_size;    //16位窗口大小  
	unsigned short chk_sum;     //16位TCP检验和  
	unsigned short urgt_p;      //16为紧急指针  
}tcp_hdr;  

//udp
typedef struct _udp_hdr  
{  
	unsigned short src_port; //远端口号  
	unsigned short dst_port; //目的端口号  
	unsigned short uhl;      //udp头部长度  
	unsigned short chk_sum;  //16位udp检验和  
}udp_hdr; 

void hex_echo(uint8_t *data_ptr,uint32_t len)
{
	int j,i=0;
	for(j=len-1;j>=0;j--)
	{
		printf("%02x ",*(data_ptr+j));
		if(++i == 8)
		{
			i=0;
			printf("\n");
		}
	}
	printf("\n");
}


void tcp_protocol_packet_callback(const u_char *packet_content,int head_len)
{
	tcp_hdr *tcp;
	tcp=(tcp_hdr *)(packet_content+head_len*4);
	fprintf(file,"%d\t",ntohs(tcp->src_port));
	fprintf(file,"%d\t",ntohs(tcp->dst_port));
	fprintf(file,"%s\n","tcp");
	fflush(file);
}

void udp_protocol_packet_callback(const u_char *packet_content,int head_len)
{
	tcp_hdr *tcp;
	tcp=(tcp_hdr *)(packet_content+head_len*4);
	fprintf(file,"%d\t",ntohs(tcp->src_port));
	fprintf(file,"%d\t",ntohs(tcp->dst_port));
	fprintf(file,"%s\n","udp");
	fflush(file);
}

void ip_protocol_packet_callback(u_char *argument, const struct pcap_pkthdr *packet_header, const u_char *packet_content)
{
	/* IP协议变量 */
	struct ip_header *ip_protocol;
	/* 长度 */
	u_int header_length;
	/* 偏移 */
	u_int offset;
	/* 服务质量 */
	u_char tos;
	/* 校验和 */
	u_int16_t checksum;


	/* 获得IP协议数据内容，去掉以太网头 */
	ip_protocol = (struct ip_header*)(packet_content + 14);
	/* 获得校验和 */
	checksum = ntohs(ip_protocol->ip_checksum);
	/* 获得长度 */
	header_length = ip_protocol->ip_header_length *4;
	/* 获得TOS */
	tos = ip_protocol->ip_tos;
	/* 获得偏移量 */
	offset = ntohs(ip_protocol->ip_off);

	/* 获得源IP地址 */
	fprintf(file,"%s\t", inet_ntoa(ip_protocol->ip_souce_address));
	/* 获得目的IP地址 */
	fprintf(file,"%s\t", inet_ntoa(ip_protocol->ip_destination_address));

	switch (ip_protocol->ip_protocol) /* 判断协议类型的值 */
	{
		case 6:
			/* 如果协议类型为6，表示上层协议为TCP协议 */
			tcp_protocol_packet_callback((u_char *)ip_protocol,ip_protocol->ip_header_length);
			break;
		case 17:
			/* 如果协议类型为17，表示上层协议为UDP协议 */
			udp_protocol_packet_callback((u_char *)ip_protocol,ip_protocol->ip_header_length);
			break;
		case 1:
			/* 如果协议类型为1，表示传上层协议为ICMP协议 */
			break;
		default:
			break;
	}

}

void ethernet_protocol_packet_callback(u_char *argument, const struct pcap_pkthdr *packet_header, const u_char *packet_content)
{
	/* 以太网类型 */
	u_short ethernet_type;
	/* 以太网协议变量 */
	struct ether_header *ethernet_protocol;
	u_char *mac_string;
	
	/* 获得以太网协议数据内容 */
	ethernet_protocol = (struct ether_header*)packet_content;

	/* 获得以太网类型 */
	ethernet_type = ntohs(ethernet_protocol->ether_type);
	switch (ethernet_type) /* 判断以太网类型的值 */
	{
		case 0x0800:
			/* 如果为0x0800，表示上层协议为IP协议 */
			break;
		case 0x0806:
			/* 如果为0x0806，表示上层协议为ARP协议 */
			break;
		case 0x8035:
			/* 如果为0x8035，表示上层协议为RARP协议 */
			break;
		default:
			break;
	}
	/* 获得源以太网地址 */
	mac_string = ethernet_protocol->ether_shost;
	/* 获得目的以太网地址 */
	mac_string = ethernet_protocol->ether_dhost;

	switch (ethernet_type)
	{
		case 0x0800:
			ip_protocol_packet_callback(argument, packet_header, packet_content);
			break;
		default:
			break;
	}
}

int main(int argc,char **argv)
{
	if(argc < 3)
	{
		perror("指定监听的网卡如:eth0,指定输出的文件名\n");
		return -1;
	}
	printf("prepare catch in %s\n",*(argv+1));

	file=fopen(*(argv+2),"w");

	pcap_t *pcap_handle;
	char errbuf[PCAP_ERRBUF_SIZE];

	if((pcap_handle=pcap_open_live(*(argv+1),BUFSIZ,0,1000,errbuf))==NULL)
	{
		fprintf(stderr,"Open device %s failed : %s \n",*(argv+1),errbuf);
		return -1;
	}

	pcap_loop(pcap_handle,-1,ethernet_protocol_packet_callback, NULL);
	pcap_close(pcap_handle);
}
